﻿using Microsoft.Extensions.Options;
using PmSoft.Data.Abstractions;
using PmSoft.Data.Abstractions.Attributes;
using SqlSugar;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data;
using System.Reflection;

namespace PmSoft.Data.SqlSugar;

/// <summary>
/// 实现 IUnitOfWork 接口的 SqlSugar 单元工作类。
/// 提供多数据库连接支持和统一的事务管理功能。
/// </summary>
public class SqlSugarUnitOfWork : IUnitOfWork
{
	/// <summary>
	/// 静态字段：存储数据库连接配置列表，延迟初始化以减少资源占用。
	/// </summary>
	private static List<ConnectionConfig>? _connectionConfigs;

	/// <summary>
	/// 线程锁对象：用于确保 _connectionConfigs 的线程安全初始化。
	/// </summary>
	private static readonly object _lock = new();

	/// <summary>
	/// SqlSugarClient 实例：负责数据库连接和操作的核心对象。
	/// </summary>
	private readonly SqlSugarClient _sugarClient;

	/// <summary>
	/// 标志位：指示当前实例是否已释放资源，避免重复释放。
	/// </summary>
	private bool _disposed;

	/// <summary>
	/// 构造函数：初始化 SqlSugarUnitOfWork 实例。
	/// </summary>
	/// <param name="options">SqlSugar 的配置选项，包含数据库连接信息。</param>
	/// <param name="dbNamesResolver">数据库名称解析器，用于自定义表名和列名。</param>
	public SqlSugarUnitOfWork(IOptions<SqlSugarOptions> options, IDbNamesResolver dbNamesResolver)
	{
		// 延迟初始化连接配置，仅在首次使用时创建并缓存。
		_connectionConfigs ??= InitializeConnectionConfigs(options.Value, dbNamesResolver);
		// 根据连接配置创建 SqlSugarClient 实例。
		_sugarClient = new SqlSugarClient(_connectionConfigs);
	}

	/// <summary>
	/// 初始化数据库连接配置。
	/// </summary>
	/// <param name="options">SqlSugar 配置选项，包含多个数据库连接信息。</param>
	/// <param name="dbNamesResolver">数据库名称解析器，用于映射实体和属性名称。</param>
	/// <returns>数据库连接配置列表。</returns>
	private static List<ConnectionConfig> InitializeConnectionConfigs(SqlSugarOptions options, IDbNamesResolver dbNamesResolver)
	{
		lock (_lock)
		{
			// 双重检查锁定，确保连接配置只初始化一次，避免重复创建。
			return _connectionConfigs ??= options.Connections.Select(m => new ConnectionConfig
			{
				ConfigId = m.ConfigId,                  // 配置标识，用于区分多个数据库连接
				DbType = m.DbType,                      // 数据库类型（如 MySQL、SQL Server 等）
				ConnectionString = m.ConnectionString,  // 数据库连接字符串
				IsAutoCloseConnection = m.IsAutoCloseConnection, // 是否自动关闭连接
				ConfigureExternalServices = new ConfigureExternalServices
				{
					// 配置实体属性映射规则
					EntityService = (property, column) => ConfigureEntityProperty(property, column, dbNamesResolver),
					// 配置实体表名映射规则
					EntityNameService = (type, entity) => ConfigureEntityName(type, entity, dbNamesResolver)
				}
			}).ToList();
		}
	}

	/// <summary>
	/// 配置实体属性与数据库列的映射关系。
	/// </summary>
	/// <param name="property">实体类的属性信息。</param>
	/// <param name="column">SqlSugar 的列信息对象。</param>
	/// <param name="dbNamesResolver">数据库名称解析器。</param>
	private static void ConfigureEntityProperty(PropertyInfo property, EntityColumnInfo column, IDbNamesResolver dbNamesResolver)
	{
		// 获取属性的特性，用于定义映射行为
		var primaryKeyAttribute = property.GetCustomAttribute<PrimaryKeyAttribute>();
		var notMappedAttribute = property.GetCustomAttribute<NotMappedAttribute>();
		var columnAttribute = property.GetCustomAttribute<ColumnAttribute>();

		// 如果属性标记为 NotMapped 或不可读写，则忽略该属性
		if (notMappedAttribute != null || !property.CanRead || !property.CanWrite)
		{
			column.IsIgnore = true;
			return;
		}

		// 如果属性标记为主键，设置主键和自增属性
		if (primaryKeyAttribute != null)
		{
			column.IsPrimarykey = true;
			column.IsIdentity = primaryKeyAttribute.IsIdentity;
		}

		// 设置数据库列名：优先使用 ColumnAttribute 中的名称，否则通过解析器生成
		column.DbColumnName = columnAttribute?.Name ?? ResolveName(property.Name, dbNamesResolver);
	}

	/// <summary>
	/// 配置实体类与数据库表名的映射关系。
	/// </summary>
	/// <param name="type">实体类的类型信息。</param>
	/// <param name="entity">SqlSugar 的表信息对象。</param>
	/// <param name="dbNamesResolver">数据库名称解析器。</param>
	private static void ConfigureEntityName(Type type, EntityInfo entity, IDbNamesResolver dbNamesResolver)
	{
		// 获取实体类的 TableAttribute 特性
		var attribute = type.GetCustomAttribute<TableAttribute>();
		// 设置表名：优先使用 TableAttribute 中的名称，否则通过解析器生成
		entity.DbTableName = attribute?.Name ?? ResolveName(type.Name, dbNamesResolver);
	}

	/// <summary>
	/// 解析名称，用于生成表名或列名。
	/// </summary>
	/// <param name="key">原始名称（属性名或类名）。</param>
	/// <param name="dbNamesResolver">数据库名称解析器，可为空。</param>
	/// <returns>解析后的名称，默认返回原始名称。</returns>
	private static string ResolveName(string key, IDbNamesResolver? dbNamesResolver)
	{
		return dbNamesResolver?.ResolveName(key) ?? key;
	}

	/// <summary>
	/// 获取指定配置 ID 的数据库连接实例。
	/// </summary>
	/// <param name="configId">配置标识，用于选择特定的数据库连接。</param>
	/// <returns>SqlSugarProvider 实例，用于执行数据库操作。</returns>
	public SqlSugarProvider GetConnection(object configId)
	{
		return _sugarClient.GetConnection(configId);
	}

	/// <summary>
	/// 开始一个数据库事务。
	/// </summary>
	/// <param name="isolationLevel">事务隔离级别，默认为 ReadCommitted。</param>
	public void BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted)
	{
		_sugarClient.BeginTran(isolationLevel);
	}

	/// <summary>
	/// 提交当前事务。
	/// </summary>
	public void CommitTransaction()
	{
		_sugarClient.CommitTran();
	}

	/// <summary>
	/// 回滚当前事务。
	/// </summary>
	public void RollbackTransaction()
	{
		_sugarClient.RollbackTran();
	}

	/// <summary>
	/// 异步开始一个数据库事务。
	/// </summary>
	/// <param name="isolationLevel">事务隔离级别，默认为 ReadCommitted。</param>
	/// <param name="cancellationToken">取消令牌，用于取消异步操作。</param>
	/// <returns>表示异步操作的任务。</returns>
	public Task BeginTransactionAsync(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted, CancellationToken cancellationToken = default)
	{
		return _sugarClient.BeginTranAsync(isolationLevel);
	}

	/// <summary>
	/// 异步提交当前事务。
	/// </summary>
	/// <param name="cancellationToken">取消令牌，用于取消异步操作。</param>
	/// <returns>表示异步操作的任务。</returns>
	public Task CommitTransactionAsync(CancellationToken cancellationToken = default)
	{
		return _sugarClient.CommitTranAsync();
	}

	/// <summary>
	/// 异步回滚当前事务。
	/// </summary>
	/// <param name="cancellationToken">取消令牌，用于取消异步操作。</param>
	/// <returns>表示异步操作的任务。</returns>
	public Task RollbackTransactionAsync(CancellationToken cancellationToken = default)
	{
		return _sugarClient.RollbackTranAsync();
	}

	/// <summary>
	/// 释放当前实例占用的资源。
	/// </summary>
	public void Dispose()
	{
		// 检查是否已释放，避免重复释放资源
		if (!_disposed)
		{
			// 释放 SqlSugarClient 的资源
			_sugarClient?.Dispose();
			_disposed = true;
		}
	}
}